﻿using System.Collections;
using System.Threading;
using System.Web;
using NHibernate;

namespace FT.Architecture.NHibernate.SessionManagement
{
    public class SessionProviderService : ISessionProviderService
    {
        private const string Sessionkey = "NHIBERNATE_SESSION";
        private const string StatelessSessionkey = "STATELESS_NHIBERNATE_SESSION";

        private static readonly IDictionary CacheStore = new Hashtable();
        private const string NoSessionErrorMessage = "There was no available session. You may have forgotten to start a Unit of Work";
        private const string SessionAlreadyExistsErrorMessage = "There was already an open session registered. You may have forgotten to commit or rollback a unit of work.";

        private T GetSession<T>(string keyPrefix) where T:class
        {
            var key = GetSessionKey(keyPrefix);

            if (!GetCacheStore().Contains(key))
            {
                throw new SessionProviderException(NoSessionErrorMessage, this);
            }

            var session = (T)GetCacheStore()[key];

            if (session == null)
            {
                throw new SessionProviderException(NoSessionErrorMessage, this);
            }

            return session;
        }

        public ISession GetSession()
        {
            return GetSession<ISession>(Sessionkey);
        }

        public IStatelessSession GetStatelessSession()
        {
            return GetSession<IStatelessSession>(StatelessSessionkey);
        }

        private void SetSession<T>(T session, string keyPrefix) where T : class
        {
            var key = GetSessionKey(keyPrefix);

            if (GetCacheStore().Contains(key) && GetCacheStore()[key] != null && session != null)
            {
                throw new SessionProviderException(SessionAlreadyExistsErrorMessage, this);
            }

            GetCacheStore()[key] = session;
        }

        public void SetSession(ISession session)
        {
            SetSession(session, Sessionkey);
        }

        public void SetStatelessSession(IStatelessSession session)
        {
            SetSession(session, StatelessSessionkey);
        }

        private bool HasSession(string keyPrefix)
        {
            var key = GetSessionKey(keyPrefix);
            return GetCacheStore().Contains(key) && GetCacheStore()[key] != null;
        }

        /// <summary>
        /// Determine whether there is a session currently in progress
        /// </summary>
        /// <returns></returns>
        public bool HasSession()
        {
            return HasSession(Sessionkey);
        }

        public bool HasStatelessSession()
        {
            return HasSession(StatelessSessionkey);
        }

        private static IDictionary GetCacheStore()
        {
            if (HttpContext.Current != null)
            {
                return HttpContext.Current.Items;
            }

            return CacheStore;
        }

        private string GetSessionKey(string prefix)
        {
            return string.Format("{0}-{1}", prefix, Thread.CurrentThread.ManagedThreadId);
        }
    }
}